#include "Player.h"
#include "Game.h"
#include "Renderer.h"
#include "PhysicsLogic.h"
#include "Camera.h"
#include "Sound.h"

SurfaceRenderingData Player::srdPlacementAllowed;
SurfaceRenderingData Player::srdPlacementNotAllowed;
SurfaceRenderingData Player::srdCannonSelection;

void Player::Load() { //static
	srdPlacementAllowed.SetColor(0,1,0,1);
	srdPlacementAllowed.SetTexture(Textures::TEXTURE_BARRIER_TAPE);

	srdPlacementNotAllowed.SetColor(1,0,0,1);
	srdPlacementNotAllowed.SetTexture(Textures::TEXTURE_BARRIER_TAPE);

	srdCannonSelection.SetColor(1.0f, 0.8f, 0.0f, 1.0f);
}

std::string Player::GetNameOfPlacable(PLACEABLE v) { //static
	std::string s;
	switch(v) {
		case PL_TOWER: s = "Turm"; break;
		case PL_WALL_FRONT: s = "Frontale Mauer"; break;
		case PL_WALL_SIDE: s = "Seitliche Mauer"; break;
		default: s = "(Fehler)";
	}
	return s;
}

Player::Player() {
	placementSlotX = 0;
	placementSlotZ = 0;
	placementBoxRenderPosition = zeroVec3;
	selectionAnimationValue = 0.0f;
	Reset();
}

Player::~Player() {

}

void Player::Reset() {
	selectedCannonIndex = 0;
	gold = GetStartGold();
	explosiveCannonBallsSelected = false;
	placementAllowed = false;
	buildMode = true;
	selectedPlaceable = PL_TOWER;
	SetPlacementSlot(areaCenterX, 0);
	turnsMade = 0;
}

void Player::SetSelectedCannonIndex(int index) {
	if(index >= cannons.Size() || index < 0) {
		selectedCannonIndex = -1;
	} else {
		selectedCannonIndex = index;
	}
}

int Player::GetSelectedCannonIndex() const {
	if(cannons.Empty()) {
		return -1;
	} else if(selectedCannonIndex < 0 || selectedCannonIndex >= cannons.Size()) {
		return -1;
	} else {
		return selectedCannonIndex;
	}
}

Cannon* Player::GetSelectedCannon() const {
	int index = GetSelectedCannonIndex();
	return index < 0 ? NULL : cannons[index];
}

void Player::SelectNextCannon() {
	const int n = cannons.Size();
	if(n > 0) {
		if(selectedCannonIndex < 0) {
			selectedCannonIndex = 0;
		} else {
			selectedCannonIndex = (selectedCannonIndex + 1) % n;
		}
	} else {
		selectedCannonIndex = -1;
	}
}

void Player::MovePlacementSlot(DIRECTION_2D direction) {
	if(buildMode) {
		switch(direction) {
			case D2_DOWN:  ++placementSlotZ; break;
			case D2_UP:    --placementSlotZ; break;
			case D2_RIGHT: ++placementSlotX; break;
			case D2_LEFT:  --placementSlotX; break;
		}
		KeepPlacementBoxInArea();
	}
}

void Player::MovePlacementSlot(const NxVec3& v) {
	if(buildMode) {
		placementSlotX += v.x;
		placementSlotZ += v.z;
		KeepPlacementBoxInArea();
	}
}

void Player::KeepPlacementBoxInArea() {
	const int dim = Game::GetBuildAreaDimension();

	if(placementSlotX > areaCenterX + dim) {
		placementSlotX = areaCenterX + dim;
	} else if(placementSlotX < areaCenterX - dim) {
		placementSlotX = areaCenterX - dim;
	}

	if(placementSlotZ > dim) {
		placementSlotZ = dim;
	} else if(placementSlotZ < -dim) {
		placementSlotZ = -dim;
	}
}

int Player::GetBuildingsCount() const {
	return buildings.Size();
}

void Player::PlaceHouses() {
	Player::PLACEABLE previousPlaceable = selectedPlaceable;
	int prevX = placementSlotX;
	int prevZ = placementSlotZ;

	selectedPlaceable = PL_HOUSE;
	const int housePlacementAreaDim = Game::GetBuildAreaDimension() - 2;
	const int housesCount = 4;
	int i = GetBuildingsCount();
	while(i < housesCount) {
		const int x = areaCenterX - housePlacementAreaDim + rand()%(housePlacementAreaDim*2);
		const int z = 0 - housePlacementAreaDim + rand()%(housePlacementAreaDim*2);
		SetPlacementSlot(x, z);
		CheckPlacementAllowed();
		if(IsPlacementAllowed()) {
			PlaceSelectedPlaceable();
			++i;
		}
	}
	selectedPlaceable = previousPlaceable;
	placementSlotX = prevX;
	placementSlotZ = prevZ;
}

void Player::CheckPlacementAllowed() {
	NxVec3 pos = GetPlacementBoxPosition();
	NxVec3 dim = GetPlacementBoxDimension();
	dim.x -= 0.5f;
	dim.z -= 0.5f;
	NxBounds3 bounds;
	bounds.set(pos-dim, pos+dim);
	placementAllowed = !PhysicsLogic::GetScene()->checkOverlapAABB(bounds, NX_ALL_SHAPES, 0xffffffff, NULL);
}

NxVec3 Player::GetPlacementBoxPosition() const {
	const float psd = Game::GetPlacementSnapDistance();
	const float x = int(placementSlotX) * psd;
	const float y = GetPlacementBoxDimension().y + 0.05f;
	const float z = int(placementSlotZ) * psd;
	return NxVec3(x, y, z);
}

NxVec3 Player::GetPlacementBoxDimension() const {
	const float psd = Game::GetPlacementSnapDistance();
	const float brickSize = Game::GetBrickSize();
	NxVec3 dim;
	switch(selectedPlaceable) {
		case PL_TOWER:			dim.set(psd*0.5f, brickSize*10*2, psd*0.5f); break;
		case PL_WALL_FRONT:		dim.set(psd*0.1f, brickSize* 9*2, psd*2.5f); break;
		case PL_WALL_SIDE:		dim.set(psd*2.5f, brickSize* 9*2, psd*0.1f); break;
		case PL_HOUSE:			dim.set(psd*1.5f, psd*1.5f,       psd*1.5f); break;
		default:				dim.set(0.5f,     0.5f,           0.5f);
	}
	return dim;
}

void Player::PlaceSelectedPlaceable() {
	Player::PLACEABLE p = GetSelectedPlaceable();
	const float snapDist = Game::GetPlacementSnapDistance();
	const float brickSize = Game::GetBrickSize();
	const NxVec3 pos(GetPlacementSlotX()*snapDist, 0, GetPlacementSlotZ()*snapDist);
	const NxVec3 soundSourcePos = (pos-PhysicsLogic::GetCamera().GetPosition())*0.15f;
	const NxVec3 cameraForward = PhysicsLogic::GetCameraForward();
	if(p == Player::PL_TOWER) {
		Sound::Play(Sound::SAMPLE_STONE1, 1.0f, soundSourcePos, cameraForward);
		const int wallLength = 2;
		const int wallHeight = 10;
		PhysicsLogic::CreateTower(pos, brickSize, wallLength, wallHeight);		
		const float rotZ = (Game::GetCurrentPlayerIndex() == 0 ? 0.0f : NxPiF32) + NxPiF32*0.5f;
		Cannon* cannon = new Cannon(pos + NxVec3(0.0f, wallHeight*brickSize*4+0.10f+0.2f, 0.0f), NxVec3(0,rotZ,0));
		cannons.Add(cannon);
	} else if(p == Player::PL_WALL_FRONT) {
		Sound::Play(Sound::SAMPLE_STONE1, 1.0f, soundSourcePos, cameraForward);
		const int wallLength = 12;
		const int wallHeight = 9;
		PhysicsLogic::CreateWall(pos+NxVec3(brickSize, 0.0f, 0.0f), brickSize, wallLength, wallHeight, NxPiF32*0.5f, true);
	} else if(p == Player::PL_WALL_SIDE) {
		Sound::Play(Sound::SAMPLE_STONE1, 1.0f, soundSourcePos, cameraForward);
		const int wallLength = 12;
		const int wallHeight = 9;
		PhysicsLogic::CreateWall(pos+NxVec3(0.0f, 0.0f, brickSize), brickSize, wallLength, wallHeight, 0.0f, true);
	} else if(p == Player::PL_HOUSE) {
		Building* building = new Building(pos);
		buildings.Add(building);
	}
}

int Player::GetCosts(PLACEABLE v) const {
	int costs;
	switch(v) {
		case PL_HOUSE:			costs = 1000; break;
		case PL_TOWER:			costs =  700; break;
		case PL_WALL_FRONT:		costs =  400; break;
		case PL_WALL_SIDE:		costs =  300; break;
		default:				costs =    0;
	}
	return costs;
}

void Player::SetExplosiveCannonBallsSelected(bool b) {
	explosiveCannonBallsSelected = b;
	const std::string message = StringBuilder() << std::string(explosiveCannonBallsSelected?"explosive":"normale") << " Geschosse ausgewählt";
	PhysicsLogic::HudMessage(message);
	Sound::Play(Sound::SAMPLE_KNOCK, 0.5f);
}

void Player::Draw() {
	if(Game::GetCurrentPlayer() == this) {
		if(buildMode) {
			glDisable(GL_LIGHTING);

			//Placement Box
			glPushMatrix();
			glTranslatef(placementBoxRenderPosition.x, placementBoxRenderPosition.y, placementBoxRenderPosition.z);
			Renderer::SetMaterial(placementAllowed ? srdPlacementAllowed : srdPlacementNotAllowed);
			glDisable(GL_CULL_FACE);
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			glLineWidth(100.0f/PhysicsLogic::GetCamera().GetPosition().distance(placementBoxRenderPosition));
			Renderer::DrawBox(GetPlacementBoxDimension());
			glPolygonMode(GL_FRONT, GL_FILL);
			glEnable(GL_CULL_FACE);
			glPopMatrix();
			
			//Build Area
			const float snap = Game::GetPlacementSnapDistance();
			const int dim = Game::GetBuildAreaDimension();
			const float baX = areaCenterX * snap;
			const float baY = 0.1f;
			const float baD = snap * dim;
			glLineWidth(6);
			Renderer::SetMaterial(srdPlacementAllowed);
			glBegin(GL_LINE_LOOP);
			glTexCoord2f(0, 0);
			glVertex3f(baX + baD, baY, baD);
			glTexCoord2f(0, 1);
			glVertex3f(baX + baD, baY, -baD);
			glTexCoord2f(0, 0);
			glVertex3f(baX - baD, baY, -baD);
			glTexCoord2f(0, 1);
			glVertex3f(baX - baD, baY, baD);
			glEnd();

			glEnable(GL_LIGHTING);
		} else {
			glDisable(GL_LIGHTING);

			//Cannon Selection
			Cannon* selectedCannon = GetSelectedCannon();
			if(selectedCannon) {
				glPushMatrix();
				NxVec3 pos = selectedCannon->GetGroundPosition();
				glTranslatef(pos.x, pos.y, pos.z);
				const float size = 1.5f;
				const float variationSize = 0.1f;
				glScalef(size+cos(selectionAnimationValue)*variationSize, 1, size+cos(selectionAnimationValue)*variationSize);
				const float lineLength = 0.5f;
				glLineWidth(8);
				Renderer::SetMaterial(srdCannonSelection);
				glBegin(GL_LINES);

				glVertex3f(1, 0, 1);
				glVertex3f(1, 0, 1-lineLength);
				glVertex3f(1, 0, 1);
				glVertex3f(1-lineLength, 0, 1);

				glVertex3f(-1, 0, 1);
				glVertex3f(-1, 0, 1-lineLength);
				glVertex3f(-1, 0, 1);
				glVertex3f(-1+lineLength, 0, 1);

				glVertex3f(-1, 0, -1);
				glVertex3f(-1, 0, -1+lineLength);
				glVertex3f(-1, 0, -1);
				glVertex3f(-1+lineLength, 0, -1);

				glVertex3f(1, 0, -1);
				glVertex3f(1, 0, -1+lineLength);
				glVertex3f(1, 0, -1);
				glVertex3f(1-lineLength, 0, -1);

				glEnd();
				glPopMatrix();
			}

			glEnable(GL_LIGHTING);
		}
	}
}

void Player::Tick(float elapsedTime) {
	selectionAnimationValue += 5.0f * elapsedTime;

	const NxVec3 boxPos = GetPlacementBoxPosition();
	NxVec3 dir(boxPos - placementBoxRenderPosition);
	const float boxSpeed = 0.5f * dir.magnitude();
	if(dir.magnitude() >= boxSpeed) {
		dir.setMagnitude(boxSpeed);
		placementBoxRenderPosition += dir;
	} else {
		placementBoxRenderPosition = boxPos;
	}

	CheckPlacementAllowed();
}

void Player::ToggleBuildMode() {
	if(buildMode == true && turnsMade <= 0) {
		PhysicsLogic::HudMessage("Angriffsmodus erst im nächsten Zug verfügbar!");
	} else if(buildMode == true && cannons.Empty()) {
		PhysicsLogic::HudMessage("Kanone für Angriffsmodus erforderlich!");
	} else {
		buildMode = !buildMode;
		PhysicsLogic::HudMessage(std::string(buildMode?"Baumodus":"Angriffsmodus"));
		Sound::Play(Sound::SAMPLE_KNOCK, 0.5f);
	}
}

void Player::SetPlaceable(PLACEABLE v, bool message) {
	selectedPlaceable = v;
	if(message) {
		PhysicsLogic::HudMessage(StringBuilder() << GetNameOfPlacable(selectedPlaceable) << " ausgewählt");
		Sound::Play(Sound::SAMPLE_KNOCK, 0.5f);
	}
}
